// ! Two memory operation access continuously the same set, second access will 
//   read fault flags and replacement info from memory, we need a buffer to store
//   flags and info updated by previous access. This buffer should be added!!!


module tag_mem
#(
// Set the size of way for a N-way set associative cache
parameter NUM_WAY = 4,            // NUM_WAY = 2**WAY_WIDTH,
// Set the size of SET, default SET size is 2**SET_WIDTH
parameter SET_WIDTH = 10,           // SET_WIDTH=ADDR_MEM_WIDTH - ADDR_CACHE_WIDTH - WAY_WIDTH,
// Set the width of TAG
parameter TAG_WIDTH = 11           //ADDR_MEM_WIDTH - CACHE_LINE_WIDTH - SET_WIDTH
)

//----------------------------------------------------
//IO port declarations
//----------------------------------------------------
(
    input   clk,
    input   rst,
    input   valid_arb2tag,
    output  ready_arb2tag,

    input   [NUM_WAY-1:0]   way_replace,

    input   [SET_WIDTH-1:0] set_rqst,
    input   [TAG_WIDTH-1:0] tag_rqst,

    input   en_update,
    input   empty_update,
    input   valid_update,
    input   dirty_update, 
    input   lock_update,
    input   [SET_WIDTH-1:0] set_update,
    input   [TAG_WIDTH-1:0] tag_update,

    output  reg empty_o, 
    output  reg valid_o, 
    output  reg dirty_o, 
    output  reg lock_o,  
    output      cache_hit,   // hit flag of each way
    output  [NUM_WAY-1:0]   way_hit
    );


//----------------------------------------------------
//Wire and reg declarations
//----------------------------------------------------
wire  [0:NUM_WAY-1]   empty_way ;
wire  [0:NUM_WAY-1]   valid_way ;
wire  [0:NUM_WAY-1]   dirty_way ;
wire  [0:NUM_WAY-1]   lock_way  ;
wire  [TAG_WIDTH-1:0] tag_cache  [0:NUM_WAY-1] ;
wire  [TAG_WIDTH+3:0] data_rd_mem [0:NUM_WAY-1] ;

wire  [TAG_WIDTH-1:0] tag_rqst_gated;
wire  [SET_WIDTH-1:0] set_rqst_gated;
wire  [TAG_WIDTH+4-1:0] data_wr_mem;

genvar  i;  // Generate loop varibale


//-----------------------------------------------------
// Memory implementation 
//-----------------------------------------------------

//Implementation of N memorys for cache tag (N-Ways)
generate for ( i=0 ; i <= NUM_WAY-1; i = i+1) begin 
    sim_mem_2p 
    #(.ADDR_WIDTH(SET_WIDTH),.DATA_WIDTH(TAG_WIDTH+4),.DEPTH(2**SET_WIDTH)) tag_cache_mem 
    (   
        .clk (clk),
        .ce0 (1'b1),
        .a0  (set_rqst_gated),
        .do0 (data_rd_mem[i] ),
        .ce1 (1'b1),
        .we1 (en_update),
        .a1  (set_update),
        .di1 (data_wr_mem) 
    );
end
endgenerate


//Split data written from cache tag memory
generate for ( i=0 ; i <= NUM_WAY-1; i = i+1) 
begin
    assign valid_way[i]     = data_rd_mem[i] [TAG_WIDTH+3];  
    assign empty_way[i]     = data_rd_mem[i] [TAG_WIDTH+2];  
    assign dirty_way[i]     = data_rd_mem[i] [TAG_WIDTH+1];  
    assign lock_way [i]     = data_rd_mem[i] [TAG_WIDTH];  
    assign tag_cache[i]   = data_rd_mem[i] [TAG_WIDTH-1:0];
end
endgenerate

//Compare cache tag and requested memory tag to generate hit signal
generate for ( i=0 ; i <= NUM_WAY-1; i = i+1) 
begin
    assign way_hit[i] = (valid_way[i]) ? (tag_rqst_gated == tag_cache[i]) ? 1'b1 : 1'b0 : 1'b0 ;
end
endgenerate
assign cache_hit = (|way_hit) ? 1 : 0;   // cache hit

generate if (NUM_WAY == 2) begin
end else if (NUM_WAY == 4) begin
    always @(*)
    begin
        case (way_hit|way_replace)
            4'b0001 : begin
                empty_o = empty_way[0];
                valid_o = valid_way[0];
                dirty_o = dirty_way[0];
                lock_o  = lock_way [0];
            end

            4'b0010 : begin
                empty_o = empty_way[1];
                valid_o = valid_way[1];
                dirty_o = dirty_way[1];
                lock_o  = lock_way [1];
            end
            4'b0100 : begin
                empty_o = empty_way[2];
                valid_o = valid_way[2];
                dirty_o = dirty_way[2];
                lock_o  = lock_way [2];
            end

            4'b1000 : begin
                empty_o = empty_way[3];
                valid_o = valid_way[3];
                dirty_o = dirty_way[3];
                lock_o  = lock_way [3];
            end

            default : begin
                empty_o = 0;
                valid_o = 0;
                dirty_o = 0;
                lock_o  = 0;
            end
        endcase
    end

end else if (NUM_WAY == 8) begin
end
endgenerate

assign data_wr_mem  =  {empty_update,valid_update,dirty_update,lock_update,tag_update}; 
assign set_rqst_gated = (ready_arb2tag) ? set_rqst : {SET_WIDTH{1'b0}};
assign tag_rqst_gated = (ready_arb2tag) ? tag_rqst : {TAG_WIDTH{1'b0}};


endmodule 